#include <kaleidoscope/frontend/syntactical_analysis/parser.hpp>
//=====================================================================
#include <boost/lexical_cast.hpp>
//=====================================================================
#include <kaleidoscope/common/symbol_ids.hpp>
//=====================================================================



using namespace kal;
using namespace atma::sooty;

main_parser::main_parser( const atma::sooty::lexeme_list& lexemes )
 : abstract_parser(lexemes, channels::code)
{
}

void main_parser::start_rule_impl()
{
	while (match_function())
		;
}

// how it looks:
//  - above-scope
//    - function (value: function-name)
//      - return-type
//      - parameter-list
//        - parameter (value: parameter-name)
//          - parameter-type
//          - default-value (NYI)
//      - body
// get the function's symbol ahead of time
parseme_ptr main_parser::match_function()
{	
	// first, make sure we're semi-good
	if (LA(0) != ids::function_keyword) return parseme_ptr();
	if (LA(2) != ids::identifier) return parseme_ptr();
	
	// first, jump and get name of function
	current_lexeme_iter().move(2);
	std::string function_name = current_lexeme_iter()->value.string;
	current_lexeme_iter().move(-2);
	

	// insert function node with value of it's symbol (and delve)
	parseme_ptr function_parseme
		= match_insert(ids::function_keyword, ids::function, function_name, true);
	if (!function_parseme) return parseme_ptr();
	
	// return type
	if (!match_type()) {
		undo_last_insert(); // function
		return parseme_ptr();
	}
	
	// skip over the name
	match(ids::identifier, false);
	
	// list of parameters
	if (!match_parameter_list()) {
		undo_last_insert(2); // function, return-type
		return parseme_ptr();
	}
	
	// the function's body
	if (!match_function_body()) {
		undo_last_insert(3); // function, return-type, parameter-list
		return parseme_ptr();
	}
	
	// end of function
	surface();
	
	return function_parseme;
}

parseme_ptr main_parser::match_type()
{
	parseme_ptr p = match_identifier();
	if (p) return p;
	p = match_insert(ids::int_keyword, ids::int_type, false);
	if (p) return p;
	return match_insert(ids::void_keyword, ids::void_type, false);
}

parseme_ptr main_parser::match_identifier()
{
	return match_insert(ids::identifier, ids::identifier, current_lexeme_iter()->value, false);
}

parseme_ptr main_parser::match_parameter_list()
{
	parseme_ptr list_parseme = insert(ids::parameter_list, true);
	
	// only match empty parameter list for now
	if (!match(ids::lparen, false, false)) {
		undo_last_insert(); // parameter-list
		return parseme_ptr();
	}
	
	// we can have no parameters! fail gracefully!
	if (!match_parameter()) {
		// but there MUST be closing parenthesis
		if (!match(ids::rparen, false, false)) {
			undo_last_insert(); // parameter-list
			return parseme_ptr();
		}
		surface();
		return list_parseme;
	}
	
	size_t removable = 1;
	while ( match(ids::comma, false) && match_parameter() )
		removable += 2;
	
	if (!match(ids::rparen, false, false)) {
		undo_last_insert(removable);
		return parseme_ptr();
	}
	
	surface();
	
	return list_parseme;
}

parseme_ptr main_parser::match_parameter()
{
	if (LA(1) != ids::identifier) return parseme_ptr();
	
	current_lexeme_iter().move(1);
	atma::sooty::value_t name = current_lexeme_iter()->value;
	current_lexeme_iter().move(-1);
	
	parseme_ptr parameter_parseme = insert(ids::parameter, name, true);
	
	if (!match_type()) {
		undo_last_insert();
		return parseme_ptr();
	}
	
	// skip over identifier
	current_lexeme_iter().move(1);
	
	surface();
	return parameter_parseme;
}

parseme_ptr main_parser::match_function_body()
{
	parseme_ptr body_parseme = match_insert(ids::lcurly, ids::function_body, true);
	
	for (;;)
	{
		if (!match_statement())
			break;
			
		if (!match(ids::semi, false)) {
			undo_last_insert();
			break;
		}
	}
	
	match(ids::rcurly, false, false);
	surface();
	return body_parseme;
}

parseme_ptr main_parser::match_integral_literal()
{
	// haha, only integer integrals
	parseme_ptr integral_parseme = 
		match_insert(ids::int_literal, ids::int_literal, current_lexeme_iter()->value, true);
	if (!integral_parseme) return parseme_ptr();
	insert(ids::int_type);
	surface();
	return integral_parseme;
}

parseme_ptr main_parser::match_rvalue()
{
	parseme_ptr p = match_integral_literal();
	if (p) return p;
	p = match_function_call();
	if (p) return p;
	return match_identifier();
}

parseme_ptr main_parser::match_statement()
{
	// atm, only function calls supported
	parseme_ptr p = match_function_call();
	if (p) return p;
	p = match_return_statement();
	if (p) return p;
	p = match_variable_definition();
	if (p) return p;
	return match_expression();
}

parseme_ptr main_parser::match_factor()
{
	parseme_ptr factor_node = insert(ids::expression, value_t(ids::expr::definition), true);
	if (!match_rvalue())
	{
		// '(' expression ')' is viable
		if (!(match(ids::lparen, false)
			&& match_expression()
			&& match(ids::rparen, false)))
		{
			undo_last_insert(); // factor
			return parseme_ptr();
		}
	}

	for (;;)
	{
		if (match(ids::star_sign, false))
			factor_node->value.integer = ids::expr::multiplication;
		else if (match(ids::fwd_slash, false))
			factor_node->value.integer = ids::expr::division;
		else
			break;

		if (!match_factor()) {
			undo_last_insert(2); // factor, rvalue
			return parseme_ptr();
		}
	}
/*
	if (factor_node->value.integer == ids::expr::definition)
	{
		parseme_ptr to_keep = factor_node->children.back();
		undo_last_insert(2);
		insert(to_keep->id, to_keep->value);
		return factor_node;
	}
*/
	
	surface();
	return factor_node;
}

parseme_ptr main_parser::match_expression()
{
	parseme_ptr expr_parseme = insert(ids::expression, value_t(ids::expr::definition), true);
	
	if (!match_factor()) {
		undo_last_insert(); // expression
		return parseme_ptr();
	}
	
	for (;;)
	{
		if (match(ids::plus_sign, false))
			expr_parseme->value.integer = ids::expr::addition;
		else if (match(ids::minus_sign, false))
			expr_parseme->value.integer = ids::expr::subtraction;
		else
			break;
		
		// tail recursion
		if (!match_expression()) {
			undo_last_insert(2); // expression, factor
			return parseme_ptr();
		}
		
		//parseme_ptr = current_lexeme_iter()->parent->children.
	}
	
	// no point keeping definition expressions around
	/*
	if (expr_parseme->value.integer == ids::expr::definition)
	{
		parseme_ptr to_keep = expr_parseme->children.back();
		undo_last_insert(2);
		insert(to_keep->id, to_keep->value);
		return expr_parseme;
	}
	*/
	
	surface();
	return expr_parseme;
}

parseme_ptr main_parser::match_function_call()
{
	
	
	// bare minimum lookahead before thinking it's a function call
	if (LA(0) != ids::identifier || LA(1) != ids::lparen) return parseme_ptr();
	
	// I guess??
	parseme_ptr function_call_parseme = insert(ids::function_call, current_lexeme_iter()->value, true);
	// now ignore symbol
	match(ids::identifier, false);
	// don't do anything with ()
	match(ids::lparen, false);
	
	// match arguments
	if (match_rvalue())
		while (match(ids::comma, false) && (match_rvalue() || match_function_call()) )
			;
	
	
	match(ids::rparen, false);
	//
	surface();
	
	return function_call_parseme;
}

parseme_ptr main_parser::match_return_statement()
{
	if (parseme_ptr p = match_insert(ids::return_keyword, ids::return_statement, true)) {
		match_statement();
		surface();
		return p;
	}
	return parseme_ptr();
}

static parseme_ptr SEQ = parseme::create(9000);


namespace insertion
{
	namespace actor_type {
		enum Enum {
			guard,
			insert,
			match,
			match_insert
		};
	}
	
	struct actor
	{
		friend actor guard();
		friend actor match(size_t, bool);
		friend actor insert(size_t, value_t&);
		friend actor match_insert(size_t, size_t, value_t&);
		
	private:
		bool active;
		abstract_parser* parser;
		actor_type::Enum type;
		bool do_insert;
		size_t match_id;
		size_t insert_id;
		value_t value;
		std::list<actor> children;
		std::list<actor> siblings;
		
		actor(abstract_parser* parser, actor_type::Enum type) : parser(parser), type(type), active(true) {}
		actor(abstract_parser* parser, actor_type::Enum type, size_t match_id, bool do_insert) : parser(parser), type(type), match_id(match_id), do_insert(do_insert), active(true) {}
		actor(abstract_parser* parser, actor_type::Enum type, size_t insert_id, value_t& value) : parser(parser), type(type), insert_id(insert_id), value(value), active(true), do_insert(true) {}
		actor(abstract_parser* parser, actor_type::Enum type, size_t match_id, size_t insert_id, value_t& value) : parser(parser), type(type), match_id(match_id), insert_id(insert_id), value(value), active(true), do_insert(true) {}
	public:
		actor(const actor& rhs)
		{
			*this = rhs;
			const_cast<actor&>(rhs).active = false;
		}
		
		actor& operator []( actor& g ) {
			children.swap(g.siblings);
			children.push_front(g);
			return *this;
		}

		actor& operator ,( actor& g ) {
			siblings.push_back(g);
			return *this;
		}

		// where it all takes place
		~actor() {
			if (!active) return;
			act();
		}
	
	private:
		// only act if we're a guard
		bool act()
		{
			if (type == actor_type::match) {
				if (!parser->match(match_id, do_insert, !children.empty())) {
					parser->undo_last_insert(do_insert ? 1 : 0);
					return false;
				}
			}
			else if (type == actor_type::insert) {
				if (!parser->insert(insert_id, value, !children.empty())) {
					parser->undo_last_insert();
					return false;
				}
			}
			else if (type == actor_type::match_insert) {
				if (value == value_t()) {
					if (!parser->match_insert(match_id, insert_id, !children.empty())) {
						parser->undo_last_insert();
						return false;
					}
				}
				else {
					if (!parser->match_insert(match_id, insert_id, value, !children.empty())) {
						parser->undo_last_insert();
						return false;
					}
				}
			}
			
			for (std::list<actor>::iterator i = children.begin(); i != children.end(); ++i) {
				if (!i->act()) {
					if (type != actor_type::guard && !(type == actor_type::match && !do_insert)) {
						parser->undo_last_insert();
						return false;
					}
				}
			}
			
			if (!children.empty()) parser->surface();
			return true;
		}
	};
	
	actor guard() { return actor(NULL, actor_type::guard); }
	actor match(size_t id, bool insert) { return actor(NULL, actor_type::match, id, insert); }
	actor insert(size_t id, value_t& value = value_t()) { return actor(NULL, actor_type::insert, id, value); }
	actor match_insert(size_t match_id, size_t insert_id, value_t& value = value_t())
		{ return actor(NULL, actor_type::match_insert, match_id, insert_id, value); }
}


insertion::actor test()
{
	using namespace insertion;
	return 
		guard() [
			insert(ids::variable) [
				match(ids::type, true),
				match(ids::identifier, true)
			]
		];
}











parseme_ptr main_parser::match_variable_definition()
{
	using namespace insertion;
	
	actor g = test();
	
	//undo_scope_guard g = start_undo_scope();
	//insertion_guard guard = make_insertion_guard();
	
	
	
	
	/*
	guard
		% insert(ids::variable, true)
			% match_type()
			&& match_identifier();
	insert(ids::variable, true);
	match_type() && match_identifier();
	*/
	//return guard.result();
	return parseme_ptr();
}
















using namespace atma::sooty;

std::string convert_parseme_to_string(const parseme_ptr& p)
{
	const size_t& id = p->id;
	const value_t& value = p->value;
	
	switch (id)
	{
		// parser ids
		case ids::int_type: return "int";
		case ids::expression:
			if (value.integer < ids::expr::multiplication)
				return "expression: " + boost::lexical_cast<std::string>(value.integer);
			else
				return "factor: " + boost::lexical_cast<std::string>(value.integer);
				
		case ids::identifier: return "identifier: " + value.string;
		case ids::function: return "function: " + value.string;
		case ids::parameter_list: return "parameter-list";
		case ids::return_statement: return "return-statement";
		case ids::int_literal: return "int-literal: " + boost::lexical_cast<std::string>(value.integer);
		case ids::function_body: return "function-body";
		case ids::function_call: return "function-call: " + value.string;
		case ids::parameter: return "parameter: " + value.string;
		case ids::variable: return "variable-definition";
	}
	
	return std::string();
}





//=====================================================================
bool main_parser::is_rvalue(size_t id) const
{
	switch (id)
	{
		case ids::function_call:
		case ids::identifier:
		case ids::int_literal:
			return true;
	}
	
	return false;
}



















void main_parser::print_tree() const
{
	print_tree_impl(get_root(), 0);
}

void main_parser::print_tree_impl(const parseme_ptr& p, int scope) const
{
	for (parseme_container::const_iterator i = p->children.begin(); i != p->children.end(); ++i)
	{
		for (int j = 0; j < scope; ++j)
		std::cout << " ";
		std::cout << convert_parseme_to_string(*i) << std::endl;
		print_tree_impl(*i, scope + 1);
	}
}

